#include "jsoncpppath.h"

#include <QFile>
#include <QtDebug>
#include <QQueue>
#include <QStringList>

JsonCppPath::JsonCppPath(const QString &jsonpath, QObject *parent)
    : QObject(parent)
    , m_jsonpath(jsonpath)
    , m_rootJson(new Json::Value)
{
}

JsonCppPath::~JsonCppPath()
{
    //child JsonCppPath 不会删除m_rootJson
    if(m_isAutoDelete)
        delete m_rootJson;
}

bool JsonCppPath::isNullJson()
{
    if(!m_rootJson)
        return true;

    return m_rootJson->isNull();
}

bool JsonCppPath::isModified()
{
    return m_isModified;
}

bool JsonCppPath::setValue(const QString &path, const QVariant &var)
{
    return setJsonValue(path, varianttoJsonValue(var));
}

bool JsonCppPath::setValueArg(const QString &path, const QString arg0, const QVariant &var)
{
    return setValue(path.arg(arg0), var);
}

bool JsonCppPath::setValueArg(const QString &path, int arg0, const QVariant &var)
{
    return setValue(path.arg(arg0), var);
}

bool JsonCppPath::setJsonValue(const QString &path, Json::Value var, const QString &comment, Json::CommentPlacement placement)
{
    if(Json::Value *jsonPtr = makePath(path)) {
        //set comment
        setComment(&var, comment, placement);

        //set value
        *jsonPtr = var;

        //set rootPtr modified flag
        QObject *rootPtr = this;
        while(qobject_cast<JsonCppPath *>(rootPtr->parent())) {
            rootPtr = rootPtr->parent();
        }
        qobject_cast<JsonCppPath *>(rootPtr)->m_isModified = true;

        return true;
    }
    m_errorString = "path is not existed.";
    return false;
}

bool JsonCppPath::setJsonValueArg(const QString &path, const QString &arg0, const Json::Value &var)
{
    return setJsonValue(path.arg(arg0), var);
}

JsonCppPath &JsonCppPath::append(const QString &path, const QVariant &var, const QString &comment, Json::CommentPlacement placement)
{
    setJsonValue(path, varianttoJsonValue(var), comment, placement);
    return *this;
}

JsonCppPath &JsonCppPath::appendJsonValue(const QString &path, const Json::Value &var, const QString &comment, Json::CommentPlacement placement)
{
    setJsonValue(path, var, comment, placement);
    return *this;
}

bool JsonCppPath::parse()
{
    QByteArray content;
    QFile file(m_jsonpath);
    if(!file.open(QFile::ReadOnly)) {
        m_errorString = file.errorString();
        return false;
    }
    content = file.readAll();

    return parse(content);
}

bool JsonCppPath::parse(const QByteArray &content)
{
    if(!m_rootJson) {
        return false;
    }
    if(content.isEmpty()) {
        clear();
        m_errorString = "json is empty.";
        return false;
    }

    Json::Reader reader;
    if(!reader.parse(content.begin(), content.end(), *m_rootJson)) {
        m_errorString = QString::fromStdString(reader.getFormatedErrorMessages());
        //避免解析到一半失败时，isNull返回false的情况
        clear();
        qDebug() << __FUNCTION__ << "error:" << m_errorString;
        return false;
    }

    if(!m_rootJson->isObject() && !m_rootJson->isArray()) {
        m_errorString = "json is empty.";
        return false;
    }

    return true;

}

QString JsonCppPath::errorString()
{
    return m_errorString;
}

QVariant JsonCppPath::value(const QString &path, const QVariant &defaultValue) const
{
    if(Json::Value *jsonPtr = findPath(path)) {
        return jsonValuetoVariant(*jsonPtr);
    }
    return defaultValue;
}

QVariant JsonCppPath::valueArg(const QString &path, int arg0, const QVariant &defaultValue)
{
    return value(path.arg(arg0), defaultValue);
}

QVariant JsonCppPath::valueArg(const QString &path, QString arg0, const QVariant &defaultValue)
{
    return value(path.arg(arg0), defaultValue);
}

Json::Value JsonCppPath::jsonValue(const QString &path, const Json::Value &defaultValue)
{
    if(Json::Value *jsonPtr = findPath(path)) {
        return *jsonPtr;
    }
    return defaultValue;
}

Json::Value JsonCppPath::jsonValueArg(const QString &path, QString arg0, const Json::Value &defaultValue)
{
    return jsonValue(path.arg(arg0), defaultValue);
}

Json::Value JsonCppPath::jsonValueArg(const QString &path, int arg0, const Json::Value &defaultValue)
{
    return jsonValue(path.arg(arg0), defaultValue);
}

int JsonCppPath::size(const QString &path)
{
    if(Json::Value *jsonPtr = findPath(path)) {
        return static_cast<int>(jsonPtr->size());
    }
    return 0;
}

bool JsonCppPath::remove(const QString &path)
{
    if(Json::Value *jsonPtr = findPath(path)) {
        *jsonPtr = Json::Value(Json::nullValue);
        return true;
    }
    m_errorString = "path is not existed.";
    return false;
}

void JsonCppPath::clear()
{
    if(m_rootJson) {
        *m_rootJson = Json::Value();
    }
}

QStringList JsonCppPath::keys(const QString &path)
{
    if(Json::Value *jsonPtr = findPath(path)) {
        Json::Value::Members list = jsonPtr->getMemberNames();
        QStringList rList;
        for(uint k = 0; k < list.size(); k ++){
            rList << QString::fromStdString(list[k]);
        }
        return rList;
    }
    return QStringList();
}

bool JsonCppPath::setComment(const QString &path, const QString &comment, Json::CommentPlacement placement)
{
    return setComment(findPath(path), comment, placement);
}

bool JsonCppPath::setComment(Json::Value *json, QString comment, Json::CommentPlacement placement)
{
    if(json == nullptr)
        return false;

    //update comment
    if(!comment.isEmpty()) {
        while(comment.startsWith("/"))
            comment.remove(0, 1);
        comment.prepend("//");
        //set comment
        //添加为空当commnt会打乱json当格式
        json->setComment(comment.toUtf8(), placement);
    }

    return true;
}

QByteArray JsonCppPath::toJson()
{
    if(Json::Value *jsonPtr = m_rootJson) {
        std::string json = Json::StyledWriter().write(*jsonPtr);
        return json.data();
    }
    return "";
}

QByteArray JsonCppPath::toJson(const QString &path)
{
    if(Json::Value *jsonPtr = findPath(path)) {
        std::string json = Json::StyledWriter().write(*jsonPtr);
        return json.data();
    }
    return "";
}

bool JsonCppPath::saveToFile()
{
    bool r = saveToFile(m_jsonpath);
    if(r) {
    //重置标志位，防止析构时保存, child的filepath都是空的，故没用
        m_isModified = false;
    }
    return r;
}

bool JsonCppPath::saveToFile(const QString &filepath)
{
    if(filepath.isEmpty()) {
        m_errorString = "JsonCppPath/jsonpath is empty";
        return false;
    }
    QFile file(filepath);
    if(!file.open(QFile::WriteOnly)) {
        m_errorString = file.errorString();
        return false;
    }
    file.write(toJson());
    file.close();

    return true;
}

QString JsonCppPath::fileName()
{
    return m_jsonpath;
}

QVariant JsonCppPath::jsonValuetoVariant(const Json::Value &value)
{
    switch(value.type()) {
    case Json::nullValue:     return QVariant(); ///< 'null' value
    case Json::intValue:      return value.asInt();      ///< signed integer value
    case Json::uintValue:     return value.asUInt();     ///< unsigned integer value
    case Json::realValue:     return value.asDouble();     ///< double value
    case Json::stringValue:   return QString::fromUtf8(value.asCString());   ///< UTF-8 string value
    case Json::booleanValue:  return value.asBool();  ///< bool value
    case Json::arrayValue:    ///< array value (ordered list)
    {
        QVariantList list;
        for(uint k = 0; k < value.size(); k ++) {
            list << jsonValuetoVariant(value[k]);
        }
        return list;
    }
    case Json::objectValue:   ///< object value (collection of name/value pairs).
    {
        QVariantMap map;
        Json::Value::Members keys = value.getMemberNames();
        for(uint k = 0; k < keys.size(); k ++) {
            map.insert(QString::fromStdString(keys.at(k)), jsonValuetoVariant(value[keys.at(k)]));
        }
        return map;
    }
    default:
        break;
    }
    return QVariant();
}

Json::Value JsonCppPath::varianttoJsonValue(const QVariant &value)
{
    switch(value.type()) {
    case QVariant::Invalid:  break;
    case QVariant::Int:      return Json::Value(value.toInt());
    case QVariant::UInt:     return Json::Value(value.toUInt());
    case QVariant::Double:   return Json::Value(value.toDouble());
    case QVariant::String:   return Json::Value(value.toString().toUtf8().data());
    case QVariant::ByteArray:return Json::Value(value.toByteArray().data());
    case QVariant::Bool:     return Json::Value(value.toBool());
    case QVariant::StringList:
    case QVariant::List: {
        Json::Value arr(Json::arrayValue);
        for(const QVariant &var : value.toList()) {
            arr.append(varianttoJsonValue(var));
        }
        return arr;
    }
    case QVariant::Map: {
        Json::Value obj(Json::objectValue);
        QVariantMap map = value.toMap();
        for(QVariantMap::iterator k = map.begin(); k != map.end(); k ++) {
            obj[k.key().toStdString()] = varianttoJsonValue(k.value());
        }
        return obj;
    }
    default:
        break;
    }
    return Json::Value();
}

Json::Value *JsonCppPath::findPath(const QString &path) const
{
    if(!m_rootJson)
        return nullptr;

    Json::Value *jsonPtr = m_rootJson;
    for(const QString &p : breakPath(path)) {
        if(p.startsWith("/")) {
            std::string key = p.mid(1).toStdString();
            if(jsonPtr->type() == Json::objectValue && jsonPtr->isMember(key)) {
                jsonPtr = &jsonPtr->operator[](key);
                continue;
            }
        }
        if(p.startsWith("[")) {
            uint key = p.mid(1).toUInt();
            if(jsonPtr->type() == Json::arrayValue && key < jsonPtr->size()) {
                jsonPtr = &jsonPtr->operator[](key);
                continue;
            }
        }
        return 0;
    }
    return jsonPtr;

}

Json::Value *JsonCppPath::makePath(const QString &path)
{
    if(!m_rootJson)
        return nullptr;

    Json::Value *jsonPtr = m_rootJson;
    for(const QString &p : breakPath(path)) {
        if(p.startsWith("/")) {
            //类型不同时，移除旧的节点
            if(jsonPtr->type() != Json::objectValue)
                *jsonPtr = Json::Value(Json::nullValue);

            //新加节点
            jsonPtr = &jsonPtr->operator[](p.mid(1).toStdString());
        }
        if(p.startsWith("[")) {
            //类型不同时，移除旧的节点
            if(jsonPtr->type() != Json::arrayValue)
                *jsonPtr = Json::Value(Json::nullValue);

            //新加节点
            jsonPtr = &jsonPtr->operator[](p.mid(1).toUInt());
        }
    }
    return jsonPtr;
}

bool JsonCppPath::exists(const QString &path)
{
    return nullptr != findPath(path);
}

QStringList JsonCppPath::breakPath(const QString &path) const
{
    QStringList list;
    int from = 0;
    while((from = path.indexOf(QRegExp("(?:/|\\[)"), from) + 1) > 0) {
        int to = path.indexOf(QRegExp("(?:/|\\[)"), from);
        list << path.mid(from - 1, to - from + 1);
    }
    return list;
}

QSharedPointer<JsonCppPath> JsonCppPath::getPathJsonPtr(const QString &path, bool isMakePath)
{
    new JsonCppPath("", this);
    QSharedPointer<JsonCppPath> ptr = QSharedPointer<JsonCppPath>(new JsonCppPath("", this));
    delete ptr->m_rootJson;
    ptr->m_rootJson = isMakePath ? this->makePath(path) : this->findPath(path);
    ptr->m_isAutoDelete = false;
    return ptr;
}

QSharedPointer<JsonCppPath> JsonCppPath::getChildJsonPtr(int k)
{
    QSharedPointer<JsonCppPath> ptr = QSharedPointer<JsonCppPath>(new JsonCppPath("", this));
    delete ptr->m_rootJson;
    ptr->m_rootJson = this->findPath(QString("[%1").arg(k));
    ptr->m_isAutoDelete = false;
    return ptr;
}

QList<QSharedPointer<JsonCppPath> > JsonCppPath::getChildrenJsonPtr(const QString &path)
{
    QList<QSharedPointer<JsonCppPath> > list;
    auto parentPtr =  getPathJsonPtr(path);
    if(!parentPtr->m_rootJson)
        return {};

    if(parentPtr->m_rootJson->isObject()) {
        for(auto key : this->keys(path)) {
            list << this->getPathJsonPtr(QString("%1/%2").arg(path).arg(key));
        }
    }

    if(parentPtr->m_rootJson->isArray()) {
        for(int k = 0, count = parentPtr->size(); k < count; k ++) {
            list << this->getPathJsonPtr(QString("%1[%2").arg(path).arg(k));
            //        list << parentPtr->getChildJsonPtr(k);//函数结束时，parentPtr会析构，导致child无夫类
        }
    }


    return list;
}

QSharedPointer<JsonCppPath> JsonCppPath::findInChildren(const QString &path, const QString &childPath, const QVariant &var)
{
    for(auto d : getChildrenJsonPtr(path)) {
        if(d->value(childPath) == var) {
            return d;
        }
    }
    return {};
}

JsonCppSettings::JsonCppSettings(const QString &jsonpath, QObject *parent)
    : JsonCppPath(jsonpath, parent)
{
    if(!parse()) {
        //qDebug() << __FUNCTION__ << "error:" << jsonpath << errorString();
    }
}

JsonCppSettings::~JsonCppSettings()
{
    if(m_isModified) {
        saveToFile();
    }
}

JsonCppReader::JsonCppReader(const Json::Value &json, QObject *parent)
    : JsonCppPath("", parent)
{
    *m_rootJson = json;
}

JsonCppReader::JsonCppReader(const QByteArray &json, QObject *parent)
    : JsonCppPath("", parent)
{
    parse(json);
}

JsonCppReader::JsonCppReader(const char *json, QObject *parent)
    : JsonCppPath("", parent)
{
    parse(json);
}

JsonCppReader::JsonCppReader(QObject *parent)
    : JsonCppPath("", parent)
{

}

JsonCppReader::~JsonCppReader()
{

}

JsonCppVoid::JsonCppVoid(QObject *parent)
    : JsonCppPath("", parent)
{

}

JsonCppVoid::~JsonCppVoid()
{

}

JsonCppPathReader::JsonCppPathReader(const QString &jsonpath, QObject *parent)
    : JsonCppPath(jsonpath, parent)
{
    if(!parse()) {
        //qDebug() << __FUNCTION__ << "error:" << jsonpath << errorString();
    }
}

JsonCppPathReader::~JsonCppPathReader()
{

}
